# 导入所需的库
import tkinter as tk
from tkinter.scrolledtext import ScrolledText
import numpy as np
import rasterio
from PIL import Image
from tkinter import filedialog
import os
from osgeo import gdal
from collections import Counter
# import geopandas as gpd
import glob
from rasterio.mask import mask


# 定义TestGui类，用于创建GUI界面
class TestGui(object):
    def __init__(self, init_window_name):
        # 初始化窗口和标题
        self.init_window_name = init_window_name
        self.init_window_name.title("land30m报告工具v1.0.0")
        self.init_window_name.geometry('950x560')

        # 设置字体和颜色
        self.font_title = ('行楷', 18, 'bold')
        self.font_button = ('行楷', 15, 'bold')
        self.bg_color = "#F0F8FF"
        self.fg_color = "black"

        # 创建日志框架和按钮框架
        self.log_frame = tk.Frame(self.init_window_name)
        self.log_frame.grid(padx=20, pady=10, row=0, column=0, sticky=tk.W)
        self.runs_button_frame = tk.Frame(self.init_window_name)
        self.runs_button_frame.grid(padx=20, pady=10, row=0, column=1, sticky=tk.W)
        self.runs_button_frame2 = tk.Frame(self.init_window_name)
        self.runs_button_frame2.grid(padx=20, pady=10, row=2, column=2, sticky=tk.W)

        # 创建滚动文本框用于显示日志
        self.run_log = ScrolledText(self.log_frame, font=('楷体', 13), width=40, height=25)
        self.run_log.grid(padx=50, pady=5, row=0, column=0)

        # 1. 一级分类到颜色的映射
        self.category_colors = {
            1: (0, 128, 0),  # 林地 -> 深绿色
            2: (0, 255, 0),  # 草地 -> 绿色
            3: (0, 0, 255),  # 湿地 -> 蓝色
            4: (255, 255, 0),  # 耕地 -> 黄色
            5: (255, 165, 0),  # 人工表面 -> 橙色
            6: (128, 0, 128),  # 其它 -> 紫色
        }

        # 定义一级分类的值范围
        self.category_mapping = {
            1: [101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112],  # 林地
            2: [21, 22, 23, 24],  # 草地
            3: [31, 32, 33, 34, 35, 36, 37],  # 湿地
            4: [41, 42],  # 耕地
            5: [51, 52, 53, 54],  # 人工表面
            6: [61, 62, 63, 64, 65, 66, 67, 68, 69],  # 其它
        }

        # 创建按钮，用于触发不同的功能

        """
        start_run2 按钮名称
        
        runs_button_frame 按钮的父容器
        
        text='处理配送报表' 按钮上显示的文本，即用户将看到的按钮标签
        
        font=self.font_button：这是按钮文本的字体样式
        
        fg=self.fg_color：这是按钮文本的前景色（即文本颜色）
        
        bg="#1E90FF"：这是按钮的背景色，这里设置为一种蓝色。
        
        width=15：这是按钮的宽度，以字符数为单位。
        
        command=lambda: self.sortpsorder()：这是按钮的回调函数，即当按钮被点击时执行的函数。这里使用了一个lambda表达式，它是一个匿名函数，当按钮被点击时，将调用self.sortpsorder()方法。

        """
        # self.start_run2 = tk.Button(self.runs_button_frame, text='处理配送报表', font=self.font_button,
        #                             fg=self.fg_color, bg="#1E90FF", width=15, command=lambda: self.sortpsorder())
        """
        self.start_run2.grid(padx=15, pady=10, row=0, column=1)：这行代码将按钮放置到网格布局中。grid是Tkinter中用于管理布局的方法之一，它允许你将控件放置在一个网格中。
        
            padx=15：这是按钮左右两侧的外边距。
            pady=10：这是按钮上下两侧的外边距。
            row=0：这是按钮在网格中的行位置。
            column=1：这是按钮在网格中的列位置。
        """
        # self.start_run2.grid(padx=1, pady=10, row=0, column=1)

        self.start_run4 = tk.Button(self.runs_button_frame, text='行政区tif的空间分布图', font=self.font_button,
                                    fg=self.fg_color, bg="#1E90FF", width=20, command=lambda: self.tif_png(self.category_colors))
        self.start_run4.grid(padx=1, pady=10, row=1, column=1)

        self.start_run3 = tk.Button(self.runs_button_frame, text='合成省级行政区tif', font=self.font_button,
                                    fg=self.fg_color, bg="#1E90FF", width=20, command=lambda: self.merge_tif_files_for_one())
        self.start_run3.grid(padx=1, pady=10, row=2, column=1)

        self.start_run4 = tk.Button(self.runs_button_frame, text='划分成省级以下行政区', font=self.font_button,
                                    fg=self.fg_color, bg="#1E90FF", width=20, command=lambda: self.tif_mask_by_shapefile())
        self.start_run4.grid(padx=1, pady=10, row=3, column=1)

        self.start_run4 = tk.Button(self.runs_button_frame, text='统计生态系统数据', font=self.font_button,
                                    fg=self.fg_color, bg="#1E90FF", width=20, command=lambda: self.get_category_stats())
        self.start_run4.grid(padx=1, pady=10, row=4, column=1)

        self.clear_log_button = tk.Button(self.runs_button_frame2, text='清除日志', font=self.font_button,
                                          fg=self.fg_color, bg="#1E90FF", width=10, command=self.clear_log)
        self.clear_log_button.grid(padx=1, pady=10, row=0, column=1)

        # 定义一个函数用于在日志框中打印消息

    def clear_log(self):
        self.run_log.config(state=tk.NORMAL)
        self.run_log.delete(1.0, tk.END)
        self.run_log.config(state=tk.DISABLED)

    def run_log_print(self, message):
        """
        在运行日志文本框中打印消息

        参数:
        message (str): 要打印的消息内容

        返回:
        无
        """
        # 启用日志文本框的编辑状态
        self.run_log.config(state=tk.NORMAL)
        # 在日志文本框末尾插入新消息
        self.run_log.insert(tk.END, "\n" + message + "\n")
        # 滚动到文本框的末尾以显示最新消息
        self.run_log.see(tk.END)
        # 更新文本框以反映内容变化
        self.run_log.update()
        # 禁用日志文本框的编辑状态
        self.run_log.config(state=tk.DISABLED)

    # 定义处理配送报表的函数


    def get_primary_category(self,sub_category):
        # 定义一级分类的值范围
        category_mapping = {
            1: [101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112],  # 林地
            2: [21, 22, 23, 24],  # 草地
            3: [31, 32, 33, 34, 35, 36, 37],  # 湿地
            4: [41, 42],  # 耕地
            5: [51, 52, 53, 54],  # 人工表面
            6: [61, 62, 63, 64, 65, 66, 67, 68, 69],  # 其它
        }
        for primary, sub_categories in category_mapping.items():
            if sub_category in sub_categories:
                return primary
        return 0  # 默认返回0，表示未知类别

    def tif_png(self, category_colors):
        self.run_log_print("请选择行政区tif文件...")
        tif_path = filedialog.askopenfilename(title="请选择行政区tif文件", filetypes=[("Tif files", "*.tif")])
        if tif_path:
            try:
                # self.df = pd.read_excel(file_path, engine='openpyxl')
                # 读取TIF图像
                Image.open(tif_path)
                self.run_log_print("成功读取行政区tif文件: " + tif_path)
            except Exception as e:
                self.run_log_print("读取行政区tif文件失败: " + tif_path)
                return
        self.run_log_print("请选择输出文件夹...")
        folder_path = filedialog.askdirectory(title="请选择输出文件夹")
        # 检查是否选择了文件夹
        if folder_path:
            try:
                self.run_log_print("成功选择输出文件夹: " + folder_path)
            except Exception as e:
                self.run_log_print("选择文件夹错误")
                return
        else:
            self.run_log_print("没有选择文件夹")
            return


        # 读取TIF图像
        img = Image.open(tif_path)

        # 将图像转换为灰度模式（假设原图每个像素值表示一个类别）
        img_array = np.array(img)
        self.run_log_print("读取TIF图像···")

        # 创建一个空的RGBA图像，第四个通道用于透明度
        colored_img_array = np.zeros((img_array.shape[0], img_array.shape[1], 4), dtype=np.uint8)

        # 遍历每个像素，将二级分类值转换为一级分类，然后映射到对应的颜色
        for i in range(img_array.shape[0]):
            for j in range(img_array.shape[1]):
                sub_category = img_array[i, j]
                primary_category = self.get_primary_category(sub_category)
                if primary_category != 0:  # 如果不是未知类别
                    colored_img_array[i, j, :3] = category_colors.get(primary_category, (0, 0, 0))  # RGB
                    colored_img_array[i, j, 3] = 255  # 完全不透明
                else:
                    colored_img_array[i, j] = [0, 0, 0, 0]  # 透明色

        # 创建一个PIL图像对象
        colored_img = Image.fromarray(colored_img_array, 'RGBA')

        # 保存为PNG格式
        file_name, _ = os.path.splitext(os.path.basename(tif_path))  # 分离文件名和扩展名
        png_path = folder_path + f'\\land30m_{file_name}.png'
        colored_img.save(png_path, 'PNG')
        self.run_log_print(f"图像已保存为 {png_path}")
        self.run_log_print(f"=================================")
        print(f"图像已保存为 {png_path}")
    # 主函数，用于启动GUI程序

    # 合成省级行政区tif
    def merge_tif_files_for_one(self):
        """
        合并文件夹下的所有tif文件为一个文件。
        输出文件保存在输入的文件夹下创建的merged文件夹中
        参数:
        input_path -- 输入文件夹的路径，包含所有tif文件。
        merge_name -- 输出合并文件的名称。
        """
        self.run_log_print("请选择包含land30m块的tif文件夹...")
        input_path = filedialog.askdirectory(title="请选择包含land30m块的tif文件夹")
        # 检查是否选择了文件夹
        if input_path:
            try:
                self.run_log_print("成功选择包含land30m块的tif文件夹: " + input_path)
            except Exception as e:
                self.run_log_print("选择包含land30m块的tif文件夹错误")
                return
        else:
            self.run_log_print("没有选择文件夹")
            self.run_log_print("over！")
            return

        # 获取文件夹下的所有tif文件
        input_files = glob.glob(os.path.join(input_path, '*.tif'))
        self.run_log_print(f"查找到 {len(input_files)} 个tif文件。")

        if not input_files:
            self.run_log_print("没有找到任何tif文件。")
            self.run_log_print("over！")
            return

        # 存储有效的tif文件
        inputrasfile = []

        for fn in input_files:
            infn = gdal.Open(fn, gdal.GA_ReadOnly)
            if infn is None:
                self.run_log_print(f"文件 {fn} 无法打开，跳过此文件。")
                continue
            input_proj1 = infn.GetProjection()
            if input_proj1 is None:
                self.run_log_print(f"文件 {fn} 没有有效的投影信息，跳过此文件。")
                continue
            inputrasfile.append(infn)

        # 如果没有有效的输入文件，退出函数
        if not inputrasfile:
            self.run_log_print("没有有效的输入文件，无法进行合并。")
            self.run_log_print("over！")
            return

        # 确保输出文件夹存在
        merge_name = os.path.basename(input_path) # 文件夹的名字
        output_file = os.path.join(input_path, 'merged', merge_name + '_land30m.tif')
        os.makedirs(os.path.dirname(output_file), exist_ok=True)

        # 使用gdal.Warp进行合并
        # 设置输出参数，可以指定投影信息、目标分辨率、合并方法等
        options = gdal.WarpOptions(format='GTiff')
        gdal.Warp(output_file, inputrasfile, options=options)

        self.run_log_print(f"合并完成，文件保存为: {output_file}")

    def tif_mask_by_shapefile(self, city_name='NAME_1'):
        """
        根据市级 Shapefile 裁剪 TIFF 文件，并将结果保存到指定输出目录。

        参数:
            tif_path (str): 输入的 TIFF 文件路径。
            shapefile_folder (str): 包含 Shapefile 的文件夹路径。
            output_folder (str): 输出目录路径。
            city_name (str): shp文件中的city_name字段的列名。
        """
        self.run_log_print("请选择被裁剪的tif文件...")
        tif_path = filedialog.askopenfilename(title="请选择被裁剪的tif文件", filetypes=[("Tif files", "*.tif")])
        if tif_path:
            try:
                # self.df = pd.read_excel(file_path, engine='openpyxl')
                # 读取TIF图像
                Image.open(tif_path)
                self.run_log_print("成功读取被裁剪的tif文件: " + tif_path)
            except Exception as e:
                self.run_log_print("读取被裁剪的tif文件失败: " + tif_path)
                return
        else:
            self.run_log_print("没有读取被裁剪的tif文件 ")
            self.run_log_print("over！ ")
            return

        self.run_log_print("请选择裁剪所需的shp文件夹...")
        shapefile_folder = filedialog.askdirectory(title="请选择裁剪所需的shp文件夹")

        self.run_log_print("请选择输出文件夹...")
        output_folder = filedialog.askdirectory(title="请选择输出文件夹")
        # 检查是否选择了文件夹
        if output_folder:
            try:
                self.run_log_print("成功选择输出文件夹: " + output_folder)
            except Exception as e:
                self.run_log_print("选择文件夹错误")
                return
        else:
            self.run_log_print("没有选择文件夹")
            self.run_log_print("over！ ")
            return

        # 创建输出目录（如果不存在）
        os.makedirs(output_folder, exist_ok=True)

        # 获取 TIFF 文件名（去掉路径和扩展名） 水源涵养_江西   用split按照"_"切分得到第一个字段
        tif_name_base = os.path.splitext(os.path.basename(tif_path))[0].split('_')[0]
        self.run_log_print(f"获取 TIFF 文件名:{tif_name_base}")

        # 读取 TIFF 文件并获取其 CRS
        with rasterio.open(tif_path) as src:
            tif_crs = src.crs  # 获取 TIFF 的 CRS
        self.run_log_print(f"读取 TIFF 文件并获取其 CRS")

        # 读取市级 Shapefile

        shapefiles = glob.glob(os.path.join(shapefile_folder, '*.shp'))
        # 标准化路径分隔符
        # shapefiles = [os.path.normpath(path) for path in shapefiles]  # 反斜杠
        shapefiles = [os.path.normpath(path).replace("\\", "/") for path in shapefiles]

        self.run_log_print(f"读取市级 Shapefile:{shapefiles}")

        if not shapefiles:
            self.run_log_print(f"错误: 在 {shapefile_folder} 中未找到任何 Shapefile 文件。")
            self.run_log_print("over！ ")
            return  # 提前返回

        for shp_path in shapefiles:
            # 读取每个 Shapefile
            self.run_log_print(f"读取每个 Shapefile:{shp_path}")
            try:
                import geopandas as gpd
            except ImportError as e:
                self.run_log_print(f"导入 geopandas 失败: {e}")
                return

            city_gdf = None  # 初始化 city_gdf
            try:
                city_gdf = gpd.read_file(shp_path)
                self.run_log_print(f"成功读取 Shapefile: {shp_path}")
            except ImportError as e:
                self.run_log_print(f"导入 fiona 或 pyogrio 失败: {e}")
            except Exception as e:
                self.run_log_print(f"读取 Shapefile 失败: {shp_path}, 错误信息: {e}")
                continue  # 跳过当前 Shapefile 文件，继续处理下一个

            # self.run_log_print(f"read_file:{city_gdf}")
            if city_gdf.empty:
                self.run_log_print(f"警告: Shapefile {shp_path} 为空。")
                continue  # 跳过空的 Shapefile
            else:
                self.run_log_print("没有空的 Shapefile！ ")
            # 确保坐标系一致
            if city_gdf.crs != tif_crs:
                city_gdf = city_gdf.to_crs(tif_crs)
                self.run_log_print("坐标一致")
            self.run_log_print("确保坐标系一致！ ")
            # 遍历每个市
            for index, row in city_gdf.iterrows():
                city_geom = [row['geometry']]  # geometry
                self.run_log_print("确保坐标系一致！ ")
                shp_city_name = row[city_name]

                # 动态命名输出文件
                output_filename = f"{tif_name_base}_{shp_city_name}.tif"

                # 裁剪 TIFF 文件
                with rasterio.open(tif_path) as src:  # 在这里打开数据集
                    out_image, out_transform = mask(src, city_geom, crop=True)

                    # 更新 metadata
                    out_meta = src.meta.copy()
                    out_meta.update({
                        "height": out_image.shape[1],
                        "width": out_image.shape[2],
                        "transform": out_transform
                    })

                    # 保存裁剪后的 TIFF 文件
                    output_path = os.path.join(output_folder, output_filename)

                    with rasterio.open(output_path, 'w', **out_meta) as dest:
                        dest.write(out_image)

                self.run_log_print(f"masked已保存: {output_path}")
        self.run_log_print("裁剪完毕！")

    # 读取TIF文件
    def read_tif(self, tif_path):
        with rasterio.open(tif_path) as src:
            return src.read(1)  # 读取第一波段数据

    # 获取一级分类
    def get_level_1_category(self, value):
        # 定义一级分类的值范围
        category_mapping = {
            1: [101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112],  # 林地
            2: [21, 22, 23, 24],  # 草地
            3: [31, 32, 33, 34, 35, 36, 37],  # 湿地
            4: [41, 42],  # 耕地
            5: [51, 52, 53, 54],  # 人工表面
            6: [61, 62, 63, 64, 65, 66, 67, 68, 69],  # 其它
        }
        for level_1, values in category_mapping.items():
            if value in values:
                return level_1
        return 0  # 如果没有对应的一级分类，返回0表示无效分类

    # 统计每个一级分类的像素数量及其占比
    def get_category_stats(self):

        self.run_log_print("请选择要统计的tif文件...")
        tif_path = filedialog.askopenfilename(filetypes=[("Tif files", "*.tif")])
        if tif_path:
            try:
                # self.df = pd.read_excel(file_path, engine='openpyxl')
                # 读取TIF图像
                Image.open(tif_path)
                self.run_log_print("成功读取行政区tif文件: " + tif_path)
            except Exception as e:
                self.run_log_print("读取行政区tif文件失败: " + tif_path)
                self.run_log_print("over！ ")
                return
        else:
            self.run_log_print("没有选择行政区tif文件" )
            self.run_log_print("over！ ")
            return

        data = self.read_tif(tif_path)

        # 忽略无效值（255），将其替换为0
        valid_data = np.where(data != 255, data, 0)

        # 将所有像素值映射为一级分类
        level_1_categories = np.vectorize(self.get_level_1_category)(valid_data)

        # 统计每个一级分类的数量
        counter = Counter(level_1_categories.flatten())

        # 计算总有效像素数（排除255的像素）
        total_valid_pixels = (valid_data != 0).sum()

        # 计算各一级分类的占比
        category_stats = {}
        for category, count in counter.items():
            if category != 0:  # 忽略无效类别
                category_stats[category] = {
                    'count': count,
                    'percentage': (count / total_valid_pixels) * 100
                }
        # 打印结果
        for category, stats in category_stats.items():
            self.run_log_print(f"一级分类 {category} 的点数量: {stats['count']}, 占比: {stats['percentage']:.2f}%")
        self.run_log_print("程序运行完毕！")
        return category_stats


if __name__ == '__main__':
    init_window = tk.Tk()
    init_window.attributes("-topmost", 1)  # 设置窗口始终在最顶部
    test_gui = TestGui(init_window)
    init_window.mainloop()  # 启动事件循环
